/**
 * 
 */
package com.ekingstar.framework.security;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.security.ConfigAttributeDefinition;
import org.springframework.security.intercept.web.FilterInvocation;
import org.springframework.security.intercept.web.FilterInvocationDefinitionSource;
import org.springframework.security.util.UrlMatcher;

import com.ekingstar.framework.domain.Function;
import com.ekingstar.framework.service.ChangeFunctionEvent;
import com.ekingstar.framework.service.IFunctionService;

/**
 * 
 * 
 */
public class DbFilterInvocationDefinitionSource implements
		FilterInvocationDefinitionSource, InitializingBean,ApplicationListener {
	private IFunctionService functionService;

	public void setFunctionService(IFunctionService functionService) {
		this.functionService = functionService;
	}
	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.springframework.security.intercept.ObjectDefinitionSource#getAttributes
	 * (java.lang.Object)
	 */
	private static final Set HTTP_METHODS = new HashSet(Arrays.asList(new String[] { "DELETE", "GET", "HEAD", "OPTIONS", "POST",
					"PUT", "TRACE" }));
	protected final Log logger = LogFactory.getLog(getClass());
	private Map requestMap = new LinkedHashMap();
	/** Stores request maps keyed by specific HTTP methods */
	private Map httpMethodMap = new HashMap();
	private UrlMatcher urlMatcher;
	private boolean stripQueryStringFromUrls;

	public UrlMatcher getUrlMatcher() {
		return urlMatcher;
	}

	public void setUrlMatcher(UrlMatcher urlMatcher) {
		this.urlMatcher = urlMatcher;
	}

	public boolean isStripQueryStringFromUrls() {
		return stripQueryStringFromUrls;
	}

	public void setStripQueryStringFromUrls(boolean stripQueryStringFromUrls) {
		this.stripQueryStringFromUrls = stripQueryStringFromUrls;
	}

	public ConfigAttributeDefinition getAttributes(Object object)
			throws IllegalArgumentException {
		FilterInvocation fi = (FilterInvocation) object;

		if ((object == null) || !this.supports(object.getClass())) {
			throw new IllegalArgumentException(
					"Object must be a FilterInvocation");
		}

		String url = fi.getRequestUrl();
		String method = fi.getHttpRequest().getMethod();

		return lookupAttributes(url, method);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @seeorg.springframework.security.intercept.ObjectDefinitionSource#
	 * getConfigAttributeDefinitions()
	 */

	public ConfigAttributeDefinition lookupAttributes(String url, String method) {
		//refresh();
		if (stripQueryStringFromUrls) {
			// Strip anything after a question mark symbol, as per SEC-161. See
			// also SEC-321
			int firstQuestionMarkIndex = url.indexOf("?");

			if (firstQuestionMarkIndex != -1) {
				url = url.substring(0, firstQuestionMarkIndex);
			}
		}

		if (urlMatcher.requiresLowerCaseUrl()) {
			url = url.toLowerCase();

			if (logger.isDebugEnabled()) {
				logger.debug("Converted URL to lowercase, from: '" + url
						+ "'; to: '" + url + "'");
			}
		}

		ConfigAttributeDefinition attributes = null;

		Map methodSpecificMap = (Map) httpMethodMap.get(method);

		if (methodSpecificMap != null) {
			attributes = lookupUrlInMap(methodSpecificMap, url);
		}

		if (attributes == null) {
			attributes = lookupUrlInMap(requestMap, url);
		}

		return attributes;
	}

	private ConfigAttributeDefinition lookupUrlInMap(Map requestMap, String url) {
		Iterator entries = requestMap.entrySet().iterator();

		while (entries.hasNext()) {
			Map.Entry entry = (Map.Entry) entries.next();
			Object p = entry.getKey();
			boolean matched = urlMatcher.pathMatchesUrl(p, url);

			if (logger.isDebugEnabled()) {
				logger.debug("Candidate is: '" + url + "'; pattern is " + p
						+ "; matched=" + matched);
			}

			if (matched) {
				return (ConfigAttributeDefinition) entry.getValue();
			}
		}

		return null;
	}

	 public Collection getConfigAttributeDefinitions() {
		 	//refresh();
	        return Collections.unmodifiableCollection(requestMap.values());
	  }

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.springframework.security.intercept.ObjectDefinitionSource#supports
	 * (java.lang.Class)
	 */
	public boolean supports(Class clazz) {
		
		// TODO Auto-generated method stub
		if (FilterInvocation.class.isAssignableFrom(clazz)) {
			return true;
		}
		return false;
	}
	//����ݿ�ȡ��Ȩ��
	public void refresh(){
		requestMap.clear();
		List res = functionService.getFunctionsWidthRoles();
		if (res != null && !res.isEmpty()){
			for (int i =0 ;i < res.size();i++){
				Function function = (Function)res.get(i);
				String url = function.getAddresss();
				if (url != null ){
					List canUseRoles = function.getCanUseRoles();
					ConfigAttributeDefinition cd = (ConfigAttributeDefinition)requestMap.get(urlMatcher.compile(url.trim()));
					if (cd == null){
						cd = new ConfigAttributeDefinition(new String[]{});
					}
					if (canUseRoles != null && !canUseRoles.isEmpty()){
						List atts = new ArrayList();
						List cds = (List)new ConfigAttributeDefinition((String[])canUseRoles.toArray(new String[]{})).getConfigAttributes();
						atts.addAll(cds);
						atts.addAll(cd.getConfigAttributes());
						cd = new ConfigAttributeDefinition(atts);
					}
					requestMap.put(urlMatcher.compile(url.trim()),cd);
					
				}
			}
		}
	}
	public void afterPropertiesSet() throws Exception {
		refresh();
		// TODO Auto-generated method stub

	}

	public void onApplicationEvent(ApplicationEvent event) {
		// TODO Auto-generated method stub
		if (event instanceof ChangeFunctionEvent){
			refresh();
		}
		
	}

}
